package dev;

import com.alibaba.middleware.race.sync.Constants2;
import com.alibaba.middleware.race.sync.Server;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

/**
 * Author :  Rocky
 * Date : 14/06/2017 11:14
 * Description :
 * Test :
 */
public class StoreService {

    private static final Logger log = LoggerFactory.getLogger(Server.class);

    //每条行记录的大小
    private int recordBytes;

    //存放行记录的文件的最大大小（单个文件）
    private int msgFileMaxSize;

    //每个行记录文件中能存放的行记录格式最大值
    public int msgMaxCountPerMsgFile;

    private MappedByteBuffer msgMbb;

    //文件每次扩张的大小
    private long fileIncrSize = 10 * 1024 * 1024;

    //第一个bit为1，代表是有效记录
    private static final byte DEFAULT_FLAG = Byte.MIN_VALUE;

    private Map<Integer, Integer> indexes = new TreeMap<>();

    private int validPos = 4;

    public StoreService(int recordBytes) {
        this.recordBytes = recordBytes;
        msgMaxCountPerMsgFile = ((Integer.MAX_VALUE - 4) / recordBytes);
        msgFileMaxSize = msgMaxCountPerMsgFile * recordBytes + 4;
        initMsgMbb();
    }

    private void initMsgMbb() {
        try {
            RandomAccessFile raf = new RandomAccessFile(new File(Constants2.MIDDLE_HOME, "0.msg"), "rw");
            msgMbb = raf.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, fileIncrSize);
            msgMbb.putInt(4);
            raf.close();
        } catch (Exception e) {

        }
    }

    public String getInfo() {
        String result = ("recordBytes=" + recordBytes + ",msgMaxCountPerMsgFile=" + msgMaxCountPerMsgFile + ",msgFileMaxSize=" + msgFileMaxSize);
        return result;
    }

    public void insertRecord(int pk, List<Object> colomns) {

        Integer pos = indexes.get(pk);
        if (pos == null) {
            indexes.put(pk, validPos);
            ensureMsgMbbCapacity();
            validPos += recordBytes;
        } else {
            msgMbb.position(pos);
        }

        //获取对应的行记录文件
        MappedByteBuffer targetMbb = msgMbb;

        //写标记位
        targetMbb.put(DEFAULT_FLAG);

        //存放主键
        targetMbb.putInt(pk);

        //存放其他列
        for (Object colomn : colomns) {

            //如果该列是字符串
            if (colomn instanceof byte[]) {
                saveStr(targetMbb, (byte[]) colomn);
            }
            //如果是数字列
            else {
                MiscUtils.writeMedium(targetMbb, (Integer) colomn);
            }
        }
    }

    private void saveStr(MappedByteBuffer targetMbb, byte[] strBytes) {
        int spos = targetMbb.position();

        //保存字符串到字符串文件中
        byte[] value = strBytes;
        targetMbb.put((byte) value.length);
        targetMbb.put(value);

        targetMbb.position(spos + 7);
    }

    private String readStr(MappedByteBuffer targetMbb) {
        int spos = targetMbb.position();

        //保存字符串到字符串文件中
        byte i = targetMbb.get();
        byte[] x = new byte[i];
        targetMbb.get(x);
        targetMbb.position(spos);

        return new String(x);
    }

    public void deleteRecord(Integer pk) {
        int pos = indexes.get(pk);

        msgMbb.position(pos);

        //标记为删除
        msgMbb.put((byte) 0);
    }

    public void updateRecord(int pkOldValue, int pkNewValue, Map<String, MapEntry> updatedData) {
        int pos = indexes.get(pkOldValue);

        msgMbb.position(pos + 1);

        //获取对应的行记录文件
        MappedByteBuffer targetMbb = msgMbb;

        //说明变更了主键
        if (pkOldValue != pkNewValue) {
            targetMbb.putInt(pkNewValue);
            indexes.remove(pkOldValue);
            indexes.put(pkNewValue, pos);
        } else {
            targetMbb.position(pos + 5);
        }

        //说明没有更新非主键列
        if (updatedData.size() == 0) {
            return;
        }

        for (Map.Entry<String, MapEntry> entry : updatedData.entrySet()) {
            String updatedColumnName = entry.getKey();
            String dataType = RecordMeta.fieldsDataTypeMap.get(updatedColumnName);

            //定位目标位置
            targetMbb.position(pos + RecordMeta.fieldsOffset.get(updatedColumnName));

            //如果变更的列是数字类型，则直接修改
            if (RecordMeta.NUM_DATA_TYPE.equals(dataType)) {
                //修改数字
                MiscUtils.writeMedium(targetMbb, (Integer) entry.getValue().getValue());
            }
            //如果变更的是字符串类型
            else {
                //保存字符串到字符串文件中
                saveStr(targetMbb, (byte[]) entry.getValue().getValue());
            }
        }
    }

    //保证参数指定的行记录文件有足够
    private void ensureMsgMbbCapacity() {

        msgMbb.position(validPos);

        //如果行记录文件空间不足，则进行扩张
        if (msgMbb.remaining() < recordBytes) {
            try {
                long newCapacity = msgMbb.capacity() + fileIncrSize;
                if (newCapacity > msgFileMaxSize) {
                    newCapacity = msgFileMaxSize;
                }

                RandomAccessFile raf = new RandomAccessFile(new File(Constants2.MIDDLE_HOME, "0.msg"), "rw");
                MappedByteBuffer newMbb = raf.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, newCapacity);
                raf.close();
                newMbb.position(validPos);
                msgMbb = newMbb;
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

    }

    public MappedByteBuffer getMsgMbb() {
        msgMbb.position(validPos);
        msgMbb.putInt(0, validPos);
        msgMbb.flip();
        msgMbb.position(4);
        return msgMbb;
    }

}
